/**
 * @file main.c
 * @brief 超级万年历主程序
 * @details 该程序实现了一个功能丰富的万年历系统，支持公历与农历的相互查询、月历显示、节气查询、节日查询、日期计算等功能。程序通过控制台界面与用户交互，使用多模块设计（如控制台设置、输入获取、日历计算等），并包含超时退出机制。
 */

#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <conio.h>

#include "..\include\console\console_color.h"
#include "..\include\console\console_font.h"
#include "..\include\calendar\calendar.h"
#include "..\include\calendar\calendar_table.h"
#include "..\include\system\system_time.h"
#include "..\include\system\keyboard_timeout.h"
#include "..\include\interface\display.h"
#include "..\include\interface\get_input.h"

/** @brief 用户输入的功能选项 */
int choice;

/** @brief 公历日期相关变量 */
int year, month, day, base_days, days;
GREDATE gre_date;          /**< 主公历日期结构体 */
GREDATE gre_date2;         /**< 辅助公历日期结构体1 */
GREDATE gre_date3;         /**< 辅助公历日期结构体2 */

/** @brief 农历日期结构体 */
LUNARDATE lunar_date;

/** @brief 系统时间结构体 */
LONGTIME lt;

/** @brief 输入超时时间（毫秒） */
unsigned long timeout = 100000;  // 100秒超时

/**
 * @brief 赋值公历日期变量
 * @details 将结构体中的日期信息解包到独立变量中，方便后续计算使用
 */
void assign_gre_date() {
    year = gre_date.year;
    month = gre_date.month;
    day = gre_date.day;
    base_days = gre_date.baseDays;
}

/**
 * @brief 主函数：万年历系统入口
 * @details 程序初始化后进入循环菜单，通过用户输入选择功能模块。
 *          每个功能模块对应不同的日期查询或计算逻辑，支持超时自动退出。
 *          程序使用多模块协作：
 *          - 控制台模块：负责界面显示与颜色设置
 *          - 输入模块：处理用户输入并验证有效性
 *          - 日历模块：实现公历与农历的相互转换、节气计算等
 *          - 系统模块：提供时间获取与超时检测功能
 */
int main() {
    InitConsole();  // 初始化控制台（字体、颜色、清屏）
    ShowTitle();    // 显示启动标题界面

    while (1) {
        // 变量初始化，避免脏数据
        choice = -1;
        gre_date = (GREDATE){.year=-1, .month=-1, .day=-1, .baseDays=-1};
        lunar_date = (LUNARDATE){.year=-1, .month=-1, .day=-1, .isLeapMonth=0, .daysOfMonth=-1};
        year = month = day = base_days = -1;
        days = INT_MAX;

        SetConsoleColor(BLACK_TEXT, WHITE_BG);
        system("cls");
        ShowMenu();  // 显示主功能菜单

        // 启动输入超时检测线程
        HANDLE timeoutThread = StartInputTimeoutThread(timeout);

        // 非阻塞式输入获取，带超时机制
        DWORD startTime = GetTickCount();
        while (GetTickCount() - startTime < timeout) {
            if (_kbhit()) { // 检测到按键输入
                scanf("%d", &choice);
                getchar(); // 吸收换行符
                NotifyInputCompleted(); // 通知超时线程输入已完成
                break;
            }
        }
        CleanupInputTimeoutThread(timeoutThread, 500); // 清理超时线程资源

        switch (choice) {
            case 1: {
                /** 公历转农历功能 */
                printf("公历查询农历：\n");
                GetGreDate(&gre_date);       // 获取用户输入的公历日期
                system("cls");
                ShowCalendar(gre_date);      // 显示公历月历
                GreToLunar(gre_date, &lunar_date); // 公历转农历
                ShowLunarDate(lunar_date, gre_date, true); // 显示农历日期及节气
                ShowNearJieqi(gre_date);     // 显示附近节气
                break;
            }

            case 2: {
                /** 农历转公历功能 */
                printf("农历查询公历：\n");
                GetLunDate(&lunar_date);     // 获取用户输入的农历日期
                system("cls");
                LunarToGre(lunar_date, &gre_date); // 农历转公历
                ShowLunarDate(lunar_date, gre_date, false); // 显示农历日期         
                printf("\n对应的");
                ShowGreDate(gre_date);       // 显示对应的公历日期
                ShowCalendar(gre_date);
                break;
            }

            case 3: {
                /** 显示月历功能 */
                printf("显示某年某月的月历：\n");
                GetYearAndMonth(&year, &month); // 获取年月
                system("cls");
                gre_date = (GREDATE){.year=year, .month=month, .day=1, .baseDays=JDNBaseDay(year, month, 1)};
                ShowCalendar(gre_date);      // 显示指定年月的月历
                break;
            }

            case 4: {
                /** 计算日期差（目标日期距今天） */
                printf("计算某天距今天的天数：\n");
                GetGreDate(&gre_date);       // 获取目标日期
                GetCurDate(&gre_date2);      // 获取当前日期
                days = gre_date.baseDays - gre_date2.baseDays; // 计算天数差
                ShowGreDate(gre_date);       // 显示目标日期
                printf("比 今天 ");
                ShowGreDate(gre_date2);      // 显示当前日期
                printf(days >= 0 ? "晚【%d】天\n" : "早【%d】天\n", abs(days));
                break;
            }

            case 5: {
                /** 计算距今天N天的日期 */
                GetCurDate(&gre_date);       // 获取当前日期
                assign_gre_date();           // 解包日期变量
                // 计算最大允许天数（当前日期到1840-01-01的天数差）
                int maxDays = base_days - JDNBaseDay(1840, 1, 1);
                printf("查询距今天（%d-%d-%d）的天数（范围0-%d）：", year, month, day, maxDays);
                GetDifDays(base_days, &days); // 获取用户输入的天数差
                // 计算前后日期
                lt = GetDate((double)(base_days + days));
                gre_date2 = (GREDATE){.year=lt.year, .month=lt.month, .day=lt.day, .baseDays=JDNBaseDay(lt.year, lt.month, lt.day)};
                lt = GetDate((double)(base_days - days));
                gre_date3 = (GREDATE){.year=lt.year, .month=lt.month, .day=lt.day, .baseDays=JDNBaseDay(lt.year, lt.month, lt.day)};
                // 显示结果
                printf("距离今天 ");
                ShowGreDate(gre_date);
                printf("【%d】天的日期为:\n", days);
                ShowGreDate(gre_date2);
                printf("或者为：\n");
                ShowGreDate(gre_date3);
                break;
            }

            case 6: {
                /** 计算任意两日期天数差 */
                printf("计算任意两天之间的天数差：\n\n");
                printf("第一个日期：\n");
                GetGreDate(&gre_date);       // 获取第一个日期
                printf("第二个日期：\n");
                GetGreDate(&gre_date2);      // 获取第二个日期
                days = abs(gre_date.baseDays - gre_date2.baseDays); // 计算绝对差
                ShowGreDate(gre_date);       // 显示第一个日期
                printf("与\n");
                ShowGreDate(gre_date2);      // 显示第二个日期
                printf("相差【%d】天。\n", days);
                break;
            }

            case 7: {
                /** 显示指定年份的二十四节气 */
                printf("显示二十四节气：\n");
                GetYear(&year);              // 获取年份
                ShowSolarTerms(year);        // 显示节气信息
                break;
            }

            case 8: {
                /** 节日查询功能 */
                while (1) {
                    system("cls");
                    printf("\t\t\t        显示节日\n\n");
                    printf("\t\t──────────────────────────────────────────────────\n");
                    printf("\n\t\t1、显示公历节日\t\t2、显示农历节日\n");
                    printf("\n\t\t请选择：");
                    int subChoice = 0;
                    scanf("%d", &subChoice);
                    getchar();

                    switch (subChoice) {
                        case 1: {
                            /** 公历节日查询 */
                            system("cls");
                            printf("\n\t\t\t\t 公历节日查询\n\n");
                            printf("\t\t ─────────────────────────────────────────\n\n");
                            printf("\t\t 请输入要查询的月份:   ");
                            GetMonth(&month);
                            ShowHoliday(month); // 显示指定月份的公历节日
                            break;
                        }
                        case 2: {
                            /** 农历节日查询 */
                            system("cls");
                            printf("\n\t\t\t\t 农历节日查询\n\n");
                            printf("\t\t───────────────────────────────────────────\n\n");
                            ShowLunarHoliday(); // 显示所有农历节日
                            break;
                        }
                        default:
                            printf("错误的输入！请输入1或2！\n");
                            getchar();
                            system("pause");
                            continue; // 重新循环
                    }
                    break; // 退出子菜单循环
                }
                break;
            }

            case 0: {
                /** 退出程序 */
                TermConsole(); // 重置控制台设置
                exit(0);
                break;
            }

            default:
                printf("错误的输入！请输入（0~8）间的数字！\n");
                break;
        }
        fflush(stdin);
        system("pause"); // 暂停等待用户按键
    }

    return 0;
}